package com.recognition;

import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JLabel;

import com.recognition.emergent.EmergentClient;
import com.recognition.image.SnapshotManager;
import com.recognition.swing.JImagePanel;
import com.recognition.webcam.Webcam;
import javax.swing.JRadioButton;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JSeparator;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.Popup;
import javax.swing.SpinnerNumberModel;
import javax.swing.JList;
import javax.swing.SpinnerListModel;
import javax.swing.JComboBox;
import javax.swing.filechooser.FileFilter;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class Main {

	private static Logger _log = Logger.getLogger();
	private static Main window;
	
	private JFrame frame;
	private EmergentClient client;
	private Webcam webcam;
	private SnapshotManager manager;
	
	private JButton btnConnectEmergent;
	private JButton btnDisconnect;
	private JButton btnShowLog;	
	private JButton btnConnectWebcam;
	private JButton btnReference;
	private JButton btnSnapshot;
	private JButton btnShowhide;
	private JButton btnRun;
	private JButton btnDisconnect_1;
	
	private JRadioButton rdbtnLearn;
	private JRadioButton rdbtnTest;
	
	private JImagePanel resultPanel;
	private JImagePanel snapshotPanel;
	private JImagePanel referencePanel;
	private JLabel lblReference;
	private JLabel lblSnapshot;
	private JLabel lblResult;
	private JSpinner spinner;	
	private JComboBox<String> comboBox;
	private DefaultComboBoxModel<String> _unitNames;
	
	private String lastDirectory;
	
	private int _lastUnit = 0;
	private JButton btnDelete;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) 
	{	
		resetPaths(); // Hack to reset java.library.path

		Webcam.initialize();		
		
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (ClassNotFoundException e1) {
			e1.printStackTrace();
		} catch (InstantiationException e1) {
			e1.printStackTrace();
		} catch (IllegalAccessException e1) {
			e1.printStackTrace();
		} catch (UnsupportedLookAndFeelException e1) {
			e1.printStackTrace();
		}		
		
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					window = new Main();
					window.frame.setVisible(true);					
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});		
	}

	/**
	 * Create the application.
	 */
	public Main() {
		_unitNames = new DefaultComboBoxModel<String>();
		
		initialize();	
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		frame = new JFrame();
		frame.setResizable(false);
		frame.addComponentListener(new ComponentAdapter() 
		{
			@Override
			public void componentMoved(ComponentEvent e) {
				updatePos();
			}
		});
		frame.setBounds(100, 100, 314, 633);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.getContentPane().setLayout(null);
		frame.setTitle("Recognition Client: Main Window");
		
		/**
		 * Labels
		 */
		
		JLabel lblWebcam = new JLabel("Webcam");
		lblWebcam.setBounds(10, 126, 68, 14);
		frame.getContentPane().add(lblWebcam);
		
		JLabel lblEmergen = new JLabel("Emergent");
		lblEmergen.setBounds(10, 41, 89, 14);
		frame.getContentPane().add(lblEmergen);
		
		lblReference = new JLabel("Reference");
		lblReference.setBounds(55, 587, 62, 14);
		frame.getContentPane().add(lblReference);
		
		lblSnapshot = new JLabel("Snapshot");
		lblSnapshot.setBounds(208, 587, 56, 14);
		frame.getContentPane().add(lblSnapshot);
		
		lblResult = new JLabel("Result");
		lblResult.setBounds(134, 470, 46, 14);
		frame.getContentPane().add(lblResult);
		
		JLabel lblTestName = new JLabel("Object name:");
		lblTestName.setBounds(10, 219, 68, 14);
		frame.getContentPane().add(lblTestName);
		
		JLabel lblNewLabel = new JLabel("Trial number:");
		lblNewLabel.setBounds(189, 219, 75, 14);
		frame.getContentPane().add(lblNewLabel);
		
		/**
		 * Panels
		 */
		
		resultPanel = new JImagePanel();
		resultPanel.setBounds(10, 273, 288, 196);
		frame.getContentPane().add(resultPanel);
		
		referencePanel = new JImagePanel();
		referencePanel.setBounds(10, 490, 141, 96);
		frame.getContentPane().add(referencePanel);
		
		snapshotPanel = new JImagePanel();
		snapshotPanel.setBounds(157, 490, 141, 96);
		frame.getContentPane().add(snapshotPanel);
		
		manager = new SnapshotManager(referencePanel, snapshotPanel, resultPanel);
		
		/**
		 * Buttons
		 */
		
		btnConnectEmergent = new JButton("Connect");
		btnConnectEmergent.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e) 
			{
				client = new EmergentClient("127.0.0.1");
				if (client.connect())
				{
					btnRun.setEnabled(true);
					rdbtnLearn.setEnabled(true);
					rdbtnTest.setEnabled(true);
					btnConnectEmergent.setEnabled(false);
					btnDisconnect.setEnabled(true);
				}
			}
		});
		btnConnectEmergent.setBounds(10, 66, 89, 23);
		frame.getContentPane().add(btnConnectEmergent);
		
		btnDisconnect = new JButton("Disconnect");
		btnDisconnect.setBounds(109, 66, 91, 23);
		btnDisconnect.setEnabled(false);
		btnDisconnect.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e) 
			{
				if (client == null)
					return;
				
				if (client.isConnected())
				{
					client.disconnect();
					btnRun.setEnabled(false);
					rdbtnLearn.setEnabled(false);
					rdbtnTest.setEnabled(false);
					btnDisconnect.setEnabled(false);
					btnConnectEmergent.setEnabled(true);					
				}
			}
		});
		frame.getContentPane().add(btnDisconnect);
		
		btnRun = new JButton("Run");
		btnRun.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent e) 
			{
				if (client != null)
				{
					btnRun.setEnabled(false);
					client.run();
					btnRun.setEnabled(true);
				}
			}
		});
		btnRun.setBounds(209, 65, 87, 25);
		btnRun.setEnabled(false);
		frame.getContentPane().add(btnRun);
		
		btnReference = new JButton("Reference");
		btnReference.setEnabled(false);
		btnReference.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent e) 
			{
				if (webcam == null)
					return;
				
				webcam.capturePicture(true);
				
				btnSnapshot.setEnabled(true);
			}
		});
		btnReference.setBounds(10, 185, 89, 23);
		frame.getContentPane().add(btnReference);
		
		btnSnapshot = new JButton("Snapshot");
		btnSnapshot.setEnabled(false);
		btnSnapshot.setBounds(109, 185, 91, 23);
		frame.getContentPane().add(btnSnapshot);
		btnSnapshot.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent e) 
			{
				if (webcam == null)
					return;
				
				webcam.capturePicture(false);
			}
		});
		
		btnConnectWebcam = new JButton("Connect");
		btnConnectWebcam.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent e) 
			{
				btnConnectWebcam.setEnabled(false);
				btnReference.setEnabled(true);
				btnDisconnect_1.setEnabled(true);
				btnShowhide.setEnabled(true);
				
				if (webcam == null)
					webcam = Webcam.getWebcam();
				webcam.connect();
				webcam.setVisible(true);	
				btnShowhide.setText("Hide Cam");
				updatePos();
			}
		});
		btnConnectWebcam.setBounds(10, 151, 89, 23);
		frame.getContentPane().add(btnConnectWebcam);
		
		btnDisconnect_1 = new JButton("Disconnect");
		btnDisconnect_1.setEnabled(false);
		btnDisconnect_1.setBounds(109, 151, 91, 23);
		btnDisconnect_1.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent e) 
			{
				btnConnectWebcam.setEnabled(true);
				btnReference.setEnabled(false);
				btnDisconnect_1.setEnabled(false);
				btnShowhide.setEnabled(false);
				
				if (webcam != null)
				{
					webcam.close();
					webcam.setVisible(false);
					webcam = null;
				}
					
				btnShowhide.setText("Show Cam");
			}
		});
		frame.getContentPane().add(btnDisconnect_1);
	
		btnShowLog = new JButton("Show Log");
		btnShowLog.setBounds(209, 5, 89, 23);
		frame.getContentPane().add(btnShowLog);		
		btnShowLog.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent e) 
			{
				if (Logger.showLog())
					btnShowLog.setText("Hide Log");
				else
					btnShowLog.setText("Show Log");
				
				updatePos();				
			}
		});
		
		JButton btnLoad = new JButton("Load");
		btnLoad.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) 
			{
				JFileChooser c = null;
				c = new JFileChooser();
				
				if (lastDirectory != null)
					c.setCurrentDirectory(new File(lastDirectory));
				else
					c.setCurrentDirectory(new File("."));
				c.setFileFilter(new FileFilter()
				{
					@Override
					public boolean accept(File f) {
						if (f.isDirectory() || f.getAbsolutePath().toLowerCase().endsWith(("project.cfg")))
							return true;
						else
							return false;
					}
					@Override
					public String getDescription() {	return "project.cfg";		}
					
				});
				int rVal = c.showOpenDialog(frame);
				if (rVal == JFileChooser.APPROVE_OPTION)
				{			
					lastDirectory = c.getSelectedFile().getAbsolutePath();
					loadProject(lastDirectory, c.getSelectedFile().getParent());
				}						
			}
		});
		btnLoad.setBounds(10, 5, 89, 23);
		frame.getContentPane().add(btnLoad);
		
		JButton btnSave = new JButton("Save");
		btnSave.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) 
			{
				JFileChooser c = null;
				
				c = new JFileChooser();
				
				if (lastDirectory != null)
					c.setCurrentDirectory(new File(lastDirectory));
				else
					c.setCurrentDirectory(new File("."));
				
				c.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
				c.setAcceptAllFileFilterUsed(false);
				int rVal = c.showSaveDialog(frame);
				if (rVal == JFileChooser.APPROVE_OPTION)
				{			
					lastDirectory = c.getSelectedFile().getAbsolutePath();
					saveProject(lastDirectory);
				}					
			}
		});
		btnSave.setBounds(109, 5, 91, 23);
		frame.getContentPane().add(btnSave);
		
		btnShowhide = new JButton("Show Cam");
		btnShowhide.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent e) 
			{
				if (webcam == null)
					webcam = Webcam.getWebcam();
				
				if (webcam.isVisible())
				{
					webcam.setVisible(false);		
					btnShowhide.setText("Show Cam");					
				}
				else
				{
					webcam.setVisible(true);
					btnShowhide.setText("Hide Cam");
				}
				updatePos();		
			}
		});
		btnShowhide.setActionCommand("Show Cam");
		btnShowhide.setBounds(209, 151, 89, 23);
		btnShowhide.setEnabled(false);
		frame.getContentPane().add(btnShowhide);
		
		JButton btnLoad_1 = new JButton("Load");
		btnLoad_1.setBounds(88, 244, 68, 23);
		btnLoad_1.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent e) {
				String name = (String) comboBox.getModel().getSelectedItem();	
				int trial = (Integer) spinner.getModel().getValue();
				int unit = _unitNames.getIndexOf(name);
				if (unit == -1 || !manager.loadSnapshot(unit, trial))
					_log.error("SnapshotManager: " + name + "_" + trial + " does not exist");
				else
					_log.info("SnapshotManager: Loaded " + name + "_" + trial);
			}
		});
		frame.getContentPane().add(btnLoad_1);
		
		JButton btnSave_1 = new JButton("Save");
		btnSave_1.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent e) {
				boolean newSnapshot = false;
				String name = (String) comboBox.getModel().getSelectedItem();	
				
				if (name.equals(""))
					return;
				
				int unit = _unitNames.getIndexOf(name);
				int trial = (Integer) spinner.getModel().getValue();
				
				if (unit == -1)
				{
					unit = _unitNames.getSize();
					if (unit == 10)
					{
						_log.error("SnapshotManager: At maximum amount of objects");
						return;
					}
					newSnapshot = true;
				}	
				
				if (!manager.saveSnapshot(unit, trial, name))
				{
					_log.error("SnapshotManager: Invalid reference, snapshot or result");
					return;
				}
				else if (newSnapshot)
					_unitNames.addElement(name);
				
				_log.info("SnapshotManager: Saved " + name + "_" + trial);
			}
		});
		btnSave_1.setBounds(10, 244, 68, 23);
		frame.getContentPane().add(btnSave_1);
		
		btnDelete = new JButton("Delete");
		btnDelete.setBounds(166, 244, 68, 23);
		btnDelete.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent e) {
				String name = (String) comboBox.getModel().getSelectedItem();	
				int unit = _unitNames.getIndexOf(name);
				int trial = (Integer) spinner.getModel().getValue();
				
				if (unit == -1)
				{
					_log.error("SnapshotManager: " + name + " does not exist");
					return;
				}
				
				manager.deleteSnapshot(unit, trial);
			}
		});
		frame.getContentPane().add(btnDelete);
		
		/**
		 * Radio Buttons
		 */
				
		rdbtnLearn = new JRadioButton("Learn");
		rdbtnLearn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (rdbtnLearn.isSelected())
				{
					rdbtnTest.setSelected(false);
					client.setMode(true);
				}				
			}
		});
		rdbtnLearn.setSelected(true);
		rdbtnLearn.setBounds(20, 96, 68, 23);
		rdbtnLearn.setEnabled(false);
		frame.getContentPane().add(rdbtnLearn);
		
		rdbtnTest = new JRadioButton("Test");
		rdbtnTest.setBounds(90, 96, 68, 23);
		rdbtnTest.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (rdbtnTest.isSelected())
				{
					rdbtnLearn.setSelected(false);
					client.setMode(false);
				}				
			}
		});
		rdbtnTest.setEnabled(false);
		frame.getContentPane().add(rdbtnTest);
		
		/**
		 * Separators
		 */
		
		JSeparator separator = new JSeparator();
		separator.setBounds(10, 121, 365, 6);
		frame.getContentPane().add(separator);
		
		JSeparator separator_1 = new JSeparator();
		separator_1.setBounds(10, 34, 365, 6);
		frame.getContentPane().add(separator_1);
		
		/**
		 * ComboBoxex
		 */
		
		comboBox = new JComboBox<String>(_unitNames);
		comboBox.setEditable(true);
		comboBox.setBounds(80, 216, 89, 22);
		frame.getContentPane().add(comboBox);
		
		spinner = new JSpinner();
		spinner.addPropertyChangeListener(new PropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent evt) {
				int val = (Integer) spinner.getModel().getValue();
				
				if (val == _lastUnit)
					return;
				
				_lastUnit = val;				
			}
		});
		spinner.setModel(new SpinnerNumberModel(0, 0, 19, 1));
		spinner.setBounds(263, 216, 35, 20);
		frame.getContentPane().add(spinner);
	}
	
	public static Main getWindow()
	{
		return window;
	}
	
	public void updatePos()
	{
		int x = frame.getX() + frame.getWidth() + 5;
		int y = frame.getY() - 2;
		
		if (webcam != null)
		{
			webcam.updatePos(x, y);
			y += webcam.getHeight();
		}
		
		if (_log != null)
		{
			_log.updatePos(x,y);			
		}
	}
	
	private static void resetPaths()
	{
		Class<ClassLoader>   myClass = ClassLoader.class;
		Field   myField;

		boolean bAccessible;
		
		try {
			myField = myClass.getDeclaredField("sys_paths");
			
			bAccessible = myField.isAccessible();
			if (!bAccessible) {
				myField.setAccessible(true);
			}			
			
			myField.set(myClass, null);
			
			System.setProperty("java.library.path","lib");

		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}
	
	private void saveProject(String dir)
	{		
		File directory = new File(dir);
		if (directory.exists())			
			removeSubfolders(directory);
		else
			directory.mkdir();
		
		String imageDir = dir + "/images";
		directory = new File(imageDir);	
		directory.mkdir();
		
		String tableDir = dir + "/tables";
		directory = new File(tableDir);
		directory.mkdir();
		
		manager.exportAll(dir, imageDir, tableDir);
	}
	
	private void loadProject(String cfg, String dir)
	{
		_unitNames.removeAllElements();
		
		manager.importAll(cfg, dir);		
	
		for (int key = 0; key < 10; ++key)
		{			
			String name = manager.getName(key << 8);
			if (name == null)
				continue;
			
			if (_unitNames.getIndexOf(name) == -1)
				_unitNames.addElement(name);	
		}
		
	}
	
	private void removeSubfolders(File path)
	{
		if(path.exists()) 
		{
			File[] files = path.listFiles();
		    for(int i=0; i<files.length; i++) 
		    {
		    	if(files[i].isDirectory())
		    		removeSubfolders(files[i]);
		    	else 
		    		files[i].delete();
		    }   
		}
	}

	public void popupTest(int winner) 
	{		
		if (winner == -1)
			JOptionPane.showMessageDialog(frame, "This object was not recognized", "Recognized object", JOptionPane.PLAIN_MESSAGE);
		else
			JOptionPane.showMessageDialog(frame, "This object is: " + _unitNames.getElementAt(winner), "Recognized object", JOptionPane.PLAIN_MESSAGE);
	}
}
